package com.file.system.plugin.websocket;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;
import com.file.system.entity.res.BaseCode;
import com.file.system.entity.res.CommonResult;
import com.file.system.entity.system.WebUser;
import com.file.system.tools.console.Read;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArraySet;

@ServerEndpoint(value = "/ws/asset", configurator = WebSocketConfig.class)
@Component
public class WebSocketServer {
    @PostConstruct
    public void init() {
        Read.console("websocket");
    }

    // concurrent包的线程安全Set，用来存放每个客户端对应的Session对象。
    private static CopyOnWriteArraySet<Session> sessionSet = new CopyOnWriteArraySet<Session>();


    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session) {
        sessionSet.add(session);
        sendMessage(session, CommonResult.ok().setResult("连接成功"));
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        sessionSet.remove(session);
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        // 判断消息是否为 JSON
        if (!JSONUtil.isTypeJSONObject(message)) {
            return;
        }

        // 判断 JSON 是否包含 code
        JSONObject obj = JSONObject.parseObject(message);
        if (!obj.containsKey("code")) {
            return;
        }

        String strCode = obj.getString("code");
        // 判断 code 是否为数字
        if (!NumberUtil.isInteger(strCode)) {
            return;
        }

        int code = Integer.parseInt(strCode);
        // 心跳检测回应
        if (BaseCode.Common.USER_HEARTBEAT.getCode() == code) {
            sendMessage(session, CommonResult.ok(BaseCode.Common.USER_HEARTBEAT).setResult("成功"));
        }
    }

    /**
     * 出现错误
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误：" + error.getMessage() + "，Session ID： " + session.getId());
        sessionSet.removeIf(s -> ObjectUtil.equal(s.getId(), session.getId()));
    }

    /**
     * 通过SHIRO_ID移除长连接
     *
     * @param shiroId
     * @apiNote -
     * @author 靳春晖
     */
    public static synchronized void onClose(String shiroId) {
        sessionSet.removeIf(s -> ObjectUtil.equal(s.getUserProperties().get(WebSocketConfig.SHIRO_ID), shiroId));
    }

    /**
     * 发送消息，实践表明，每次浏览器刷新，session会发生变化。
     *
     * @param session
     * @param message
     */
    private static void sendMessage(Session session, CommonResult message) {
        try {
            if (session.isOpen()) {
                String json = JSONUtil.toJsonStr(message);
                session.getBasicRemote().sendText(json);
            }
        } catch (Exception e) {
            System.out.println("发送消息出错：" + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 群发消息
     *
     * @param message
     * @throws IOException
     */
    public static synchronized void broadCastInfo(BaseCode.Common common, Object message) {
        sessionSet.forEach(session -> sendMessage(session, CommonResult.ok(common).setResult(message)));
    }


    /**
     * 群发消息  发送多次
     *
     * @param message
     * @throws IOException
     */
    public static synchronized void broadCastInfo(BaseCode.Common common, Object message, int num) {
        for (int i = 0; i < num; i++) {
            sessionSet.forEach(session -> sendMessage(session, CommonResult.ok(common).setResult(message)));
        }
    }


    /**
     * 指定user id发送消息
     *
     * @param userId  用户ID
     * @param message 消息
     */
    public static synchronized void sendMessage(BaseCode.Common common, Object message, Long userId) {
        Optional<Session> optionalSession = sessionSet.stream()
                .filter(s -> ObjectUtil.equal(((WebUser) s.getUserProperties().get(WebSocketConfig.USER)).getId(), userId))
                .findFirst();

        optionalSession.ifPresent(session -> sendMessage(session, CommonResult.ok(BaseCode.Common.MSG_ORDINARY).setResult(message)));
    }


    /**
     * 发送公告消息 指定shiro session id
     *
     * @param shiroId shiro session id
     * @param message 消息
     */
    public static synchronized void sendMessage(BaseCode.Common common, Object message, String shiroId) {
        Optional<Session> optionalSession = sessionSet.stream()
                .filter(s -> ObjectUtil.equal(s.getUserProperties().get(WebSocketConfig.SHIRO_ID), shiroId))
                .findFirst();

        optionalSession.ifPresent(session -> {
            sendMessage(session, CommonResult.ok(common).setResult(message));


            if (ObjectUtil.equals(common, BaseCode.Common.USER_OUT) ||
                    ObjectUtil.equals(common, BaseCode.Common.USER_TIME_OUT) ||
                    ObjectUtil.equals(common, BaseCode.Common.USER_OTHER_LOGIN)) {
                sessionSet.remove(session);
            }
        });
    }


}